package com.timing.feedback.filter;


import com.timing.feedback.config.JwtTokenDao;
import com.timing.feedback.entity.UserPO;
import com.timing.feedback.utils.WriteJsonUtil;
import com.timing.feedback.vo.ResultVO;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.annotation.Resource;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * @author cwh
 */
@Slf4j
@Component("jwtAuthenticationTokenFilter")
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {


    @Resource
    private JwtTokenDao jwtTokenDao;

    public final static String HEADER = "Token";

    @Override
    protected void doFilterInternal(HttpServletRequest request, @NotNull HttpServletResponse response, @NotNull FilterChain chain) throws ServletException, IOException {
        String token = request.getHeader(HEADER);
        //从请求头中获取token
        log.info("Token:{}",token);
        log.info("request getMethod :{}",request.getMethod());
        //Token不存在,或为空,
        if (!StringUtils.hasText(token)){
            chain.doFilter(request, response);
            return;
        }

        //Token是否有效
        if (!isTokenValid(token)){
            WriteJsonUtil.writeJson(request,response,ResultVO.error().message("Token无效"));
            return;
        }
        //从token中获取username
        Integer id=getUserIdFromToken(token);

        //校验redis中user是否过期
        if (isTokenExpired(id)){
            WriteJsonUtil.writeJson(request,response, ResultVO.error().message("Token过期"));
            return;
        }

        //检验redis中token是否一致,不一致说明用户已在其他地方登录
        if (isLoginElsewhere(id,token)){
            WriteJsonUtil.writeJson(request,response,ResultVO.error().message("用户已在其他地方登录"));
            return;
        }

        //认证成功,刷新redis中的token
        refreshToken(id,token);

        //设置认证信息
        saveUserDetails(id,request);

        chain.doFilter(request, response);
    }

    /**
    * 刷新redis中的token

     * @param id 用户名
     * @param token token
     */
    private void refreshToken(Integer id, String token) {
        jwtTokenDao.refreshToken(id,token);
    }


    /**
    * 将用户信息存到内存中
    */
    private void saveUserDetails(Integer id, HttpServletRequest request) {
//        UserDetails userDetails = new UserPO(id,"", "");
//        // 将用户信息存入 authentication,这里只存了username,token中不保存密码
//        UsernamePasswordAuthenticationToken authentication =
//            new UsernamePasswordAuthenticationToken(
//                userDetails,
//                null,
//                userDetails.getAuthorities()
//            );
//        authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
//        SecurityContextHolder.getContext().setAuthentication(authentication);
    }

    /**
    * 账号是否已在别处登录
    * 在别处登陆过返回true
     * @param id 用户名 token
     * @param token*/
    private boolean isLoginElsewhere(Integer id, String token) {
        return !jwtTokenDao.exist(id,token);
    }

    /**
    * 从token中获取username
    * @return:
    * @param token*/
    private Integer getUserIdFromToken(String token) {
        return jwtTokenDao.getUserIdFromToken(token);
    }

    /**
    * 检验token是否过期
    * 过期返回true
    * @return:
    * @param id*/
    private boolean isTokenExpired(Integer id) {
        return jwtTokenDao.isTokenExpired(id);
    }

    /**
    * 检验token是否有效
    * 有效返回true
    * @return:
    * @param token*/
    private boolean isTokenValid(String token) {
        return jwtTokenDao.validateToken(token);
    }

}
